package oboe.utils;

import flash.display.DisplayObject;
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Dictionary;

import oboe.core.Controller;
import oboe.core.Oboe;

class Prototype
{
private static var hasOboeExtensions:Bool = false;

public static function addMethod( c:Class, name:String, func:Function ):Void
{
	c.prototype[name] = func;
	c.prototype.setPropertyIsEnumerable( name, false );
}

public static function addOboeExtensions():Void
{
	if( hasOboeExtensions ) return;
	
	addMethod( DisplayObject, "getController", function ():Controller {
		return Oboe.controllerLookup[this];
	});
	
	addMethod( Object, "toDictionary", function ( weakKeys : Bool=false ) : Dictionary {
		var ret : Dictionary = new Dictionary( weakKeys );
		for( i in Reflect.fields(this) ){
			ret[i] = this[i];
		}
		return ret;
	});
	
	addMethod( Object, "$", function():String{ return "X"; });
	
	addMethod( URLLoader, "loadEx", function( url:URLRequest ):Void{
		this.addEventListener( IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):Void{ trace( "Error downloading: " + url.url ); } );
		this.load( url );
	});
	
	addMethod( Loader, "loadEx", function( url:URLRequest ):Void{
		this.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):Void{ trace( "Error downloading: " + url.url ); } );
		this.load( url );
	});
	
	addMethod( EventDispatcher, "removeCallback", function( e:CallbackHandle ):Void
	{
		this.removeEventListener( e.event, e.cb );
	});
	
	addMethod( EventDispatcher, "addCallback", function( e:String, f:Function, args:Array<Dynamic> ):CallbackHandle
	{
		var r:CallbackHandle = new CallbackHandle(e);
		
		var target:EventDispatcher = this;
		
		var cb:Function = function (ev:Event):Void 
		{
			if( r.ignoreCount )
			{
			r.ignoreCount--;
			return;
			}
			if( r.once )
			{
			Prototype.removeCallback( target, r );
			}
			f.apply( null, args );
		};

		this.addEventListener( e, cb );

		r.cb = cb;
		
		return r;
	});

	addMethod( EventDispatcher, "addCallbackEx", function( e:String, f:Function, args:Array<Dynamic> ):CallbackHandle
	{
		var r:CallbackHandle = new CallbackHandle(e);

		var target:EventDispatcher = this;
		
		var cb:Function = function (ev:Event):Void 
		{
			if( r.ignoreCount )
			{
			r.ignoreCount--;
			return;
			}
			if( r.once )
			{
			Prototype.removeCallback( target, r );
			}
			args.unshift( ev );
			f.apply( null, args );
			args.shift();
		};
		
		this.addEventListener( e, cb );
		
		r.cb = cb;
		
		return r;
	});
	
	hasOboeExtensions = true;
}

public static function addCallback( target:EventDispatcher, e:String, f:Function, args:Array<Dynamic> ):CallbackHandle
{
	var r:CallbackHandle = new CallbackHandle(e);

	var cb:Function = function (ev:Event):Void 
	{
	if( r.ignoreCount )
	{
		r.ignoreCount--;
		return;
	}
	if( r.once )
	{
		Prototype.removeCallback( target, r );
	}
	f.apply( null, args );
	};
	
	target.addEventListener( e, cb );
	
	r.cb = cb;
	
	return r;
}

public static function addCallbackEx( target:EventDispatcher, e:String, f:Function, args:Array<Dynamic> ):CallbackHandle
{
	var r:CallbackHandle = new CallbackHandle(e);
	
	var cb:Function = function (ev:Event):Void 
	{
	if( r.ignoreCount )
	{
		r.ignoreCount--;
		return;
	}
	if( r.once )
	{
		Prototype.removeCallback( target, r );
	}
	args.unshift( ev );
	f.apply( null, args );
	args.shift();		
	};
	
	target.addEventListener( e, cb );
	r.cb = cb;
	
	return r;
}

public static function removeCallback( target:EventDispatcher, e:CallbackHandle ):Void
{
	target.removeEventListener( e.event, e.cb );
}
}